﻿using System;
using System.Collections.Generic;

using Outcoder.Concurrency;

namespace Outcoder.CommandModel
{
	/// <summary>
	/// Handles management and dispatching of EventHandlers 
	/// that are maintained in a weakly referenced list.
	/// </summary>
	internal static class WeakEventHandlerManager
	{
		/// <summary>
		/// Invokes each of the handlers.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="handlers"></param>
		public static void CallWeakReferenceHandlers(
			object sender, List<WeakReference> handlers)
		{
			if (handlers != null)
			{
				/* Make a copy of the array incase it is modified. */
				var callees = new EventHandler[handlers.Count];
				int count = 0;

				count = CleanupOldHandlers(handlers, callees, count);

				for (int i = 0; i < count; i++)
				{
					CallHandler(sender, callees[i]);
				}
			}
		}

		static void CallHandler(object sender, EventHandler eventHandler)
		{
			if (eventHandler != null)
			{
				ISynchronizationContext uiContext = UISynchronizationContext.Instance;
				if (uiContext.InvokeRequired)
				{
					uiContext.InvokeAndBlockUntilCompletion(
						() => eventHandler(sender, EventArgs.Empty));
				}
				else
				{
					eventHandler(sender, EventArgs.Empty);
				}
			}
		}

		static int CleanupOldHandlers(
			List<WeakReference> handlers, EventHandler[] callees, int count)
		{
			for (int i = handlers.Count - 1; i >= 0; i--)
			{
				WeakReference reference = handlers[i];
				var handler = reference.Target as EventHandler;
				if (handler == null)
				{
					// Handler has been garbage collected, therefore it is removed.
					handlers.RemoveAt(i);
				}
				else
				{
					callees[count] = handler;
					count++;
				}
			}
			return count;
		}

		/// <summary>
		/// Adds a handler to the supplied list. 
		/// If the list is null, a new list is created.
		/// </summary>
		/// <param name="handlers">If null it will be created.</param>
		/// <param name="handler">Handler that will be invoked 
		/// when <c>CallWeakReferenceHandlers</c> is called.</param>
		/// <param name="defaultListSize">Default list size.</param>
		public static void AddWeakReferenceHandler(
			ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
		{
			if (handlers == null)
			{
				handlers = (defaultListSize > 0
					? new List<WeakReference>(defaultListSize)
					: new List<WeakReference>());
			}

			handlers.Add(new WeakReference(handler));
		}

		/// <summary>
		/// Removes an event handler from the supplied reference list.
		/// </summary>
		/// <param name="handlers">Handler list to remove reference from.</param>
		/// <param name="handler">Handler to remove.</param>
		public static void RemoveWeakReferenceHandler(
			List<WeakReference> handlers, EventHandler handler)
		{
			if (handlers != null)
			{
				for (int i = handlers.Count - 1; i >= 0; i--)
				{
					WeakReference reference = handlers[i];
					var existingHandler = reference.Target as EventHandler;
					if (existingHandler == null || existingHandler == handler)
					{
						// Clean up old handlers that have been collected
						// in addition to the handler that is to be removed.
						handlers.RemoveAt(i);
					}
				}
			}
		}
	}
}
